import itertools

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Voronoi, voronoi_plot_2d
from sklearn.neighbors import KDTree
from shapely.geometry import Polygon, Point
import networkx as nx
from heapq import heappop, heappush
import cv2
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import halcon as hv



class Trans2Graph():
    def __init__(self,):
        self.file = './resource/img_1.png'
        self.fromWeb = True


        self.nodes = []
        self.edges = []
        self.edge = []


        self.num = 0
        self.stop = False

    def getMask(self,road_image,fromWeb):
        def percentage_filter(image, neighborhood_size=3):
            # 使用膨胀操作获取邻域内的最大值
            max_filtered = cv2.erode(image, np.ones((neighborhood_size, neighborhood_size), np.uint8))
            return max_filtered

        if not fromWeb:
            road_image2 = percentage_filter(road_image, neighborhood_size=3)
            # road_image = cv2.blur(road_image, (3, 3), 0)

        # 二值化图像
        # 设定颜色阈值，提取道路区域
        lower_bound = np.array([0, 0, 0], dtype=np.uint8)
        upper_bound = np.array([120, 120, 120], dtype=np.uint8)
        road_mask = cv2.inRange(road_image, lower_bound, upper_bound)

        if not fromWeb:
            # kernel = np.ones((3, 3), np.uint8)
            # road_mask = cv2.dilate(road_mask, kernel, iterations=4)  # 膨胀
            # road_mask = cv2.ximgproc.thinning(road_mask)
            cv2.imshow("PP", road_mask)

            # 进行腐蚀和膨胀操作，去除噪声
            kernel = np.ones((3, 3), np.uint8)
            road_mask = cv2.morphologyEx(road_mask, cv2.MORPH_OPEN, kernel)
            road_mask = cv2.morphologyEx(road_mask, cv2.MORPH_CLOSE, kernel)

            kernel = np.ones((5, 5), np.uint8)
            road_mask2 = cv2.dilate(road_mask, kernel, iterations=5)  # 膨胀
            # road_mask2 = cv2.GaussianBlur(road_mask2, (7, 7), 0)

            binary_image = cv2.erode(road_mask2, np.ones((3, 3), np.uint8), iterations=7)  # 腐蚀
            return binary_image
        return road_mask


    def getGuJia(self, file = './resource/demo2.png'):
        def skeletonize(image):
            size = np.size(image)
            skel = np.zeros(image.shape, np.uint8)

            ret, img = cv2.threshold(image, 127, 255, 0)
            element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
            done = False

            while not done:
                eroded = cv2.erode(img, element)
                temp = cv2.dilate(eroded, element)
                temp = cv2.subtract(img, temp)
                skel = cv2.bitwise_or(skel, temp)
                img = eroded.copy()

                zeros = size - cv2.countNonZero(img)
                if zeros == size:
                    done = True
            return skel

        # 读取道路图像
        road_image = cv2.imread(file, cv2.IMREAD_COLOR)

        road_mask = self.getMask(road_image,self.fromWeb)

        # 提取图像骨架
        # skeleton = skeletonize(binary_image)
        skeleton = cv2.ximgproc.thinning(road_mask)#如果图像来自前端绘图，则直接使用，无需膨胀腐蚀
        #cv2.imshow("PP",skeleton)

        return skeleton

    #找(x, y)点的8邻域
    def neighbours(self, x, y, image):
        img = image
        x_1, y_1, x1, y1 = x - 1, y - 1, x + 1, y + 1
        return [img[x_1][y], img[x_1][y1], img[x][y1], img[x1][y1], img[x1][y], img[x1][y_1], img[x][y_1],
                img[x_1][y_1]]

    def getSkeletonIntersection(self, skeleton):
        # A biiiiiig list of valid intersections             2 3 4
        # These are in the format shown to the right         1 C 5
        #                                                    8 7 6
        valid_intersection = [[0, 1, 0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0, 1], [1, 0, 0, 1, 0, 1, 0, 0],
                             [0, 1, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 0, 1, 0, 1], [1, 0, 0, 1, 0, 0, 1, 0],
                             [0, 1, 0, 0, 1, 0, 0, 1], [1, 0, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0, 1],
                             [0, 1, 0, 1, 0, 0, 0, 1], [0, 1, 0, 1, 0, 1, 0, 0], [0, 0, 0, 1, 0, 1, 0, 1],
                             [1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0],
                             [1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 0, 0, 1, 1, 1],
                             [1, 1, 0, 0, 1, 0, 0, 1], [0, 1, 1, 1, 0, 0, 1, 0], [1, 0, 1, 1, 0, 0, 1, 0],
                             [1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 1, 0, 1, 1, 0], [0, 1, 1, 0, 1, 0, 1, 1],
                             [1, 1, 0, 1, 1, 0, 1, 0], [1, 1, 0, 0, 1, 0, 1, 0], [0, 1, 1, 0, 1, 0, 1, 0],
                             [0, 0, 1, 0, 1, 0, 1, 1], [1, 0, 0, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 0, 1],
                             [1, 0, 1, 0, 1, 1, 0, 0], [1, 0, 1, 0, 1, 0, 0, 1], [0, 1, 0, 0, 1, 0, 1, 1],
                             [0, 1, 1, 0, 1, 0, 0, 1], [1, 1, 0, 1, 0, 0, 1, 0], [0, 1, 0, 1, 1, 0, 1, 0],
                             [0, 0, 1, 0, 1, 1, 0, 1], [1, 0, 1, 0, 0, 1, 0, 1], [1, 0, 0, 1, 0, 1, 1, 0],
                             [1, 0, 1, 1, 0, 1, 0, 0]]

        valid_endofline = [[1, 0, 0, 0, 0, 0, 0, 0],[0, 1, 0, 0, 0, 0, 0, 0],[0, 0, 1, 0, 0, 0, 0, 0],
                           [0 ,0 ,0 ,1 ,0 ,0 ,0 ,0],[0, 0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 0, 0, 1, 0, 0],
                           [0, 0, 0, 0, 0, 0, 1, 0],[0, 0, 0, 0, 0, 0, 0, 1]]
        image = skeleton.copy()
        image = image / 255
        intersections = list()
        endodlines = list()
        for x in range(1, len(image) - 1):
            for y in range(1, len(image[x]) - 1):
                # If we have a white pixel
                if image[x][y] == 1:
                    neighbours = self.neighbours(x, y, image)
                    valid = True;
                    if neighbours in valid_intersection:
                        intersections.append((x, y))
                    if neighbours in valid_endofline:
                        endodlines.append((x, y))
        # #过滤距离过近的交点
        # for point1 in intersections:
        #     for point2 in intersections:
        #         if (((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) < 10 ** 2) and (point1 != point2):
        #             intersections.remove(point2)
        #
        # #过滤距离过近的交点
        # for point1 in intersections:
        #     for point2 in intersections:
        #         if (((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) < 10 ** 2) and (
        #                 point1 != point2):
        #             intersections.remove(point2)
        # # 去重
        # intersections = list(set(intersections))
        print("骨架图的尺寸：")
        print(skeleton.shape)
        print("交点个数：")
        print(len(intersections))
        print("端点个数：")
        print(len(endodlines))
        return intersections,endodlines

    def buildEdge(self, skeleton, is_passed, point, nodes, num):
        x = point[0]
        y = point[1]
        x_1, y_1, x1, y1 = x - 1, y - 1, x + 1, y + 1

        # if point == (212,421):
        #     print([skeleton[x_1][y], skeleton[x_1][y1], skeleton[x][y1], skeleton[x1][y1], skeleton[x1][y],
        #            skeleton[x1][y_1], skeleton[x][y_1],
        #            skeleton[x_1][y_1]])
        #
        #     print([is_passed[x_1][y], is_passed[x_1][y1], is_passed[x][y1], is_passed[x1][y1], is_passed[x1][y], is_passed[x1][y_1], is_passed[x][y_1],
        #         is_passed[x_1][y_1]])

        neighbours = [(x_1, y), (x, y1), (x1, y), (x, y_1), (x_1, y1), (x1, y1), (x1, y_1), (x_1, y_1)]
        # print(neighbours)


        for (x_neighbour, y_neighbour) in neighbours:


            if (x_neighbour, y_neighbour) in nodes:
                point1 = self.edge[0]
                point2 = (x_neighbour, y_neighbour)
                if len(self.edge) < 5:#若路径太短，则换路
                    # for (x, y) in self.edge:
                    #     is_passed[x][y] = False
                    continue
                else:
                    print("到节点了"+ str((x_neighbour, y_neighbour)))
                    self.edge.append((x_neighbour, y_neighbour))
                    # is_passed[x_neighbour][y_neighbour] = True
                    return True

            if skeleton[x_neighbour][y_neighbour] == 255 and  is_passed[x_neighbour][y_neighbour] == False:
                # if self.edge[0] in [(214, 422), (180, 387)]:
                #     print(x_neighbour, y_neighbour)
                # print('迭代'+ str(num)+  '边：'+str((x_neighbour, y_neighbour)))
                self.edge.append((x_neighbour, y_neighbour))
                is_passed[x_neighbour][y_neighbour] = True
                stop = self.buildEdge(skeleton, is_passed, (x_neighbour, y_neighbour), self.nodes, num+1)
                return

        print(
            [skeleton[x_1][y], skeleton[x][y1], skeleton[x1][y], skeleton[x][y_1], skeleton[x_1][y1], skeleton[x1][y1],
             skeleton[x][y_1],
             skeleton[x_1][y_1]])

        print([[is_passed[x_1][y], is_passed[x][y1], is_passed[x1][y], is_passed[x][y_1], is_passed[x_1][y1], is_passed[x1][y1],
             is_passed[x][y_1],
             is_passed[x_1][y_1]]])


        neighbours = [(x_1, y), (x, y1), (x1, y), (x, y_1), (x_1, y1), (x1, y1), (x1, y_1), (x_1, y_1)]

        print(neighbours)


    #根据骨架图和节点将边拆分出来
    def splitEdge(self, skeleton, nodes = []):

        # skeleton = self.getGuJia(self.file)

        #is_passed用来记录某个像素是否已经划分到某边
        is_passed =  np.full(skeleton.shape, False, dtype=bool)
        edges = []
        #遍历每个关键节点，并从节点出发深度优先遍历各像素，直到碰到另一个节点
        for (x,y) in nodes:
            # is_passed[x][y] = True
            #向四周发散
            x_1, y_1, x1, y1 = x - 1, y - 1, x + 1, y + 1
            neighbours = [(x_1, y), (x, y1), (x1, y), (x, y_1), (x_1, y1), (x1, y1), (x1, y_1), (x_1, y_1)]
            for (x_neighbour ,y_neighbour) in neighbours:
                self.edge = []
                self.num = 0;
                self.stop = False
                self.edge.append((x, y))

                if skeleton[x_neighbour][y_neighbour] == 255 and  is_passed[x_neighbour][y_neighbour] == False:
                    print("开始于---------------------------------------------------------------------" + str((x, y)))
                    self.edge.append((x_neighbour, y_neighbour))
                    is_passed[x_neighbour][y_neighbour] = True
                    self.buildEdge(skeleton, is_passed, (x_neighbour ,y_neighbour), self.nodes,self.num)
                    if len(self.edge) > 3:
                        edges.append(self.edge)

                # print(self.edge)
        return edges


    def draw(self):
        # 提取图像骨架
        skeleton = self.getGuJia(self.file)
        intertions,endoflines = self.getSkeletonIntersection(skeleton)
        self.nodes = intertions + endoflines

        print("关键点坐标如下：")
        print(self.nodes)

        self.edges = self.splitEdge(skeleton, self.nodes)

        print("边的个数：")
        print(len(self.edges))

        # print(self.edges[20])

        # print((skeleton[889][213]))

        #绘制
        skeleton = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2BGR)

        point_size = 2
        thickness = 2
        point_color_insec = (100, 255, 44)  # 交点2
        point_color_endp = (100, 44, 255)  # 端点
        point_color_edge = [(123,200,10),(100,50,250),(180,36,153),(90,130,200),(180,200,36),(50,100,255)]

        #画交点
        print(intertions)
        for i,point in enumerate(intertions):
            cv2.circle(skeleton, (point[1], point[0]), point_size, point_color_edge[i%5], thickness)
        # 画端点
        for point in endoflines:
            cv2.circle(skeleton, (point[1], point[0]), point_size + 1, point_color_endp, thickness)

        # cv2.circle(skeleton, (803,528), 2, (255,255,255), 2)

        cv2.imshow("PP", skeleton)
        cv2.waitKey(0)

        #画边
        for i,edge in enumerate(self.edges):
            # cv2.circle(skeleton, (edge[0][1], edge[0][0]), 2, (255,0,0), 2)
            # cv2.circle(skeleton, (edge[-1][1], edge[-1][0]), 2, (255,0,0), 2)


            # print(edge[-1])

            for point in edge:
                if point is not edge[0] and point is not edge[-1]:
                    skeleton[point[0]][point[1]] = point_color_edge[i%6]
                    # cv2.circle(skeleton, (point[1], point[0]), 1, point_color_edge[i%6], 1)
            # cv2.imshow("PP", skeleton)
            # cv2.waitKey(0)

        # image = hv.read_image(self.path)
        #
        # crosses = hv.skeleton_to_cross(skeleton)
        # junctions = hv.skeleton_to_junctions(skeleton)
        #
        # # 获取交点和端点
        # hv.set_system('flush_graphic', 'true')
        # hv.set_draw('margin')
        # hv.set_color('red')
        # hv.skeleton(skeleton)
        #
        # # 显示结果
        # hv.disp_image(image)
        # hv.disp_continue(0)
        # hv.disp_continue(1)
        #
        # # 关闭 Halcon 窗口
        # hv.close_window()
        cv2.imshow("PP", skeleton)
        cv2.waitKey(0)

